//
//  UnixMD5Crypt.m
//  HelloWorld
//
//  Created by Martin Le C. on 5/20/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "UnixMD5Crypt.h"
@implementation UnixMD5Crypt
+(void)to64:(NSMutableString*)sb n:(NSInteger)n nCount:(NSInteger)nCount {
	NSString * sITOA64 = @"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	const char* ITOA64 = [sITOA64 UTF8String];
	while (--nCount >= 0) {
		[sb appendFormat:@"%c",ITOA64[n&0x3f]];
		n>>=6;
	}
}
+(NSString*)crypt:(NSString*)strPassword strSalt:(NSString*)strSalt {	
	@try{ 	
		const char* MAGIC = "$1$";
		const char* abyPassword = [strPassword UTF8String];
		NSArray* st = [strSalt componentsSeparatedByString:@"$"];
		const char* abySalt = [[st objectAtIndex:2] UTF8String];
		CC_MD5_CTX tempContext; 
		CC_MD5_Init( &tempContext );
		CC_MD5_Update( &tempContext, abyPassword, strlen(abyPassword) );
		CC_MD5_Update( &tempContext, MAGIC, 3 );
		CC_MD5_Update( &tempContext, abySalt, strlen(abySalt) );
		//CC_MD5_Final( pTempKey, &tempContext );
		
		
		CC_MD5_CTX tempContext2;
		CC_MD5_Init( &tempContext2 );
		CC_MD5_Update( &tempContext2, abyPassword, strlen(abyPassword) );
		CC_MD5_Update( &tempContext2, abySalt, strlen(abySalt));
		CC_MD5_Update( &tempContext2, abyPassword, strlen(abyPassword) );
		unsigned char abyFinal[17]		= { 0, };
		// then create the response based on that challenge
		// zero out the hash buffer in advance
		bzero( abyFinal, 17 );
		CC_MD5_Final(abyFinal, &tempContext2);
		for(NSInteger n= strlen(abyPassword); n>0; n-=16)
		{	
			CC_MD5_Update( &tempContext, abyFinal, n>16 ? 16 : n );
		}
		unsigned char abyFinal2[1] = {0};
		
		// "Something really weird"
		// Not sure why 'j' is here as it is always zero, but it's in Kamp's code too
		for(int i=strlen(abyPassword); i!=0; i>>=1 )
		{	
			if( (i&1)==1 )  
				CC_MD5_Update( &tempContext, abyFinal2, 1);
			else  
				CC_MD5_Update( &tempContext, abyPassword, 1);
		}
		
		// Build the output string
		NSMutableString* sbPasswd = [[NSMutableString alloc]init]; 
		[sbPasswd appendString:[NSString stringWithFormat:@"$1$%@$",[NSString stringWithUTF8String:abySalt]]];
		// then create the response based on that challenge
		//unsigned char	pHash[17]		= { 0, };
		// zero out the hash buffer in advance
		//bzero( abyFinal3, 17 );
		CC_MD5_Final(abyFinal, &tempContext);
		// And now, just to make sure things don't run too fast
		// in C . . . "On a 60 Mhz Pentium this takes 34 msec, so you would
		// need 30 seconds to build a 1000 entry dictionary..."
		for(int n=0;n<1000;n++)
		{	
			CC_MD5_CTX tempContext3;
			CC_MD5_Init( &tempContext3 );
			//MD5Init(&ctx1);
			if((n & 1)!=0) {
				CC_MD5_Update( &tempContext3, abyPassword, strlen(abyPassword)); 
			}
			else {
				CC_MD5_Update(&tempContext3, abyFinal, 16);
			}
			if((n % 3)!=0) {
			CC_MD5_Update(&tempContext3, abySalt, strlen(abySalt)); }
			
			if((n % 7)!=0) {
				CC_MD5_Update(&tempContext3,abyPassword,strlen(abyPassword));
			}
			if((n & 1)!=0) {
				CC_MD5_Update(&tempContext3, abyFinal, 16); 
			}
			else 
				CC_MD5_Update(&tempContext3, abyPassword, strlen(abyPassword));
			CC_MD5_Final(abyFinal, &tempContext3);
		}
		
		// Convert to int's so we can do our bit manipulation
		// it's a bit tricky making the byte act unsigned
		const int anFinal[] = {	
			(abyFinal[ 0] & 0x7f) | (abyFinal[ 0] & 0x80) , (abyFinal[ 1] & 0x7f) | (abyFinal[ 1] & 0x80) ,
			(abyFinal[ 2] & 0x7f) | (abyFinal[ 2] & 0x80) , (abyFinal[ 3] & 0x7f) | (abyFinal[ 3] & 0x80) ,
			(abyFinal[ 4] & 0x7f) | (abyFinal[ 4] & 0x80) , (abyFinal[ 5] & 0x7f) | (abyFinal[ 5] & 0x80) ,
			(abyFinal[ 6] & 0x7f) | (abyFinal[ 6] & 0x80) , (abyFinal[ 7] & 0x7f) | (abyFinal[ 7] & 0x80) ,
			(abyFinal[ 8] & 0x7f) | (abyFinal[ 8] & 0x80) , (abyFinal[ 9] & 0x7f) | (abyFinal[ 9] & 0x80) ,
			(abyFinal[10] & 0x7f) | (abyFinal[10] & 0x80) , (abyFinal[11] & 0x7f) | (abyFinal[11] & 0x80) ,
			(abyFinal[12] & 0x7f) | (abyFinal[12] & 0x80) , (abyFinal[13] & 0x7f) | (abyFinal[13] & 0x80) ,
			(abyFinal[14] & 0x7f) | (abyFinal[14] & 0x80) , (abyFinal[15] & 0x7f) | (abyFinal[15] & 0x80)
		};
		[UnixMD5Crypt to64:sbPasswd n:(anFinal[0]<<16 | anFinal[ 6]<<8 | anFinal[12]) nCount:4];
		[UnixMD5Crypt to64:sbPasswd n:(anFinal[ 1]<<16 | anFinal[ 7]<<8 | anFinal[13]) nCount:4];
		[UnixMD5Crypt to64:sbPasswd n:(anFinal[ 2]<<16 | anFinal[ 8]<<8 | anFinal[14]) nCount:4];
		[UnixMD5Crypt to64:sbPasswd n:(anFinal[ 3]<<16 | anFinal[ 9]<<8 | anFinal[15]) nCount:4];
		[UnixMD5Crypt to64:sbPasswd n:(anFinal[ 4]<<16 | anFinal[10]<<8 | anFinal[ 5]) nCount:4];
		[UnixMD5Crypt to64:sbPasswd n:(anFinal[11]) nCount:2];
		return sbPasswd;
	}
	@catch (NSException* e) { 
		NSLog(@"%@",[e reason]); 
	}
	return nil;
}
@end